﻿using JGSY.SerialCommunication.UpperComputer.Component;
using JGSY.SerialCommunication.UpperComputer.Core;
using JGSY.SerialCommunication.UpperComputer.Core.EnumType;
using JGSY.SerialCommunication.UpperComputer.IComponent;
using RJCP.IO.Ports;
using System;
using System.Collections.Generic;

using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JGSY.SerialCommunication.UpperComputer.Modbus
{
    public class ModbusRtu : ModbusBase
    {
        private ModbusRtu() { }
        /// <summary>
        /// 添加构造函数注入通信单元
        /// </summary>
        /// <param name="portName">串口号，根据实际情况修改</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="dataBit">数据位</param>
        /// <param name="parity">校验位</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="readTimeout">读取超时时间</param>
        public ModbusRtu(string portName, int baudRate, int dataBit, Parity parity, StopBits stopBits, int readTimeout = 50)
        {
            CommunicationUnit = new Serial(portName, baudRate, dataBit, parity, stopBits, readTimeout);
        }

        public override byte[] SendRequest(List<byte> pdu)
        {
            if (pdu == null || pdu.Count == 0)
                throw new ArgumentNullException(nameof(pdu), "PDU不能为空");

            //一、Serial管理操作类，打开SerialPort  
            Result<bool> connectState = CommunicationUnit.Open(100);
            if (!connectState.Status)
            {
                return new byte[0];
            }

            // 计算CRC校验 大端序结果
            ushort crc = ModbusCrcCalculator.CalculateCrc(pdu.ToArray());
            byte[] crcBytes = BitConverter.GetBytes(crc);

            // 构建完整的Modbus RTU消息 (PDU + CRC)
            byte[] request = new byte[pdu.Count + 2];
            Array.Copy(pdu.ToArray(), 0, request, 0, pdu.Count);
            Array.Copy(crcBytes, 0, request, pdu.Count, 2);

            var repContent = ModbusLengthCalculator.ExtractDataFromRequest(ModbusProtocolType.Rtu, request.ToArray());
            int functionCode = repContent.FunctionCode;
            int dataCount = repContent.DataCount;
            int receiveLen = ModbusLengthCalculator.CalculateNormalResponseLength(ModbusProtocolType.Rtu, functionCode, dataCount);

            int errrorLen = ModbusLengthCalculator.CalculateExceptionResponseLength(ModbusProtocolType.Rtu);


            // 计算CRC校验并且 构建完整的Modbus RTU消息 (PDU + CRC)
            //var request = CRC16(pdu);
            // 使用通信单元发送请求并接收响应
            var result = ((Serial)CommunicationUnit).SendAndRecevice(request.ToList(), receiveLen, errrorLen);

            if (!result.Status)
                throw new InvalidOperationException($"通信失败: {result.Message}");

            List<byte> responseBuffer = result.Datas;


            if (responseBuffer.Count < 3) // 至少需要功能码(1) + 数据长度(1) + 至少1字节数据 + CRC(2)
                throw new InvalidDataException("接收到的响应长度不足");

            // 验证CRC
            byte[] responseWithoutCrc = responseBuffer.Take(responseBuffer.Count - 2).ToArray();
            //byte[] receivedCrc = responseBuffer.Skip(responseBuffer.Count - 2).Take(2).ToArray();

            //ushort calculatedCrc = ModbusCrcCalculator.CalculateCrc(responseWithoutCrc);
            //byte[] calculatedCrcBytes = BitConverter.GetBytes(calculatedCrc);


            //if (!receivedCrc.SequenceEqual(calculatedCrcBytes))
            //    throw new InvalidDataException("CRC校验失败");

            // 返回不包含CRC的PDU部分
            return responseWithoutCrc;


        }


        /// <summary>
        /// 在CRC-16/MODBUS校验算法中，`0xFFFF`、`0xA001`和`0xFF`这些值并不是随意选择的，
        /// 它们是算法中固定的参数，具有特定的含义和作用。这些值不能随意更改，否则会导致计算结果与标准的CRC-16/MODBUS校验值不一致。
        /// </summary>
        /// <param name="value">报文CRC前的bytes集合</param>
        /// <param name="poly">生成多项式的反向表示，固定。</param>
        /// <param name="crcInit">初始值，固定。</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static List<byte> CRC16(List<byte> value, ushort poly = 0xA001, ushort crcInit = 0xFFFF)
        {
            if (value == null || !value.Any()) { throw new ArgumentException(""); }

            //0xFF`**：掩码，固定。
            ushort crc = crcInit;
            for (int i = 0; i < value.Count; i++)
            {
                crc = (ushort)(crc ^ (value[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
                }
            }
            //十六进制掩码的含义
            //(1) 0xFF00 二进制表示：11111111 00000000 作用：用于提取 高字节（High Byte）。 
            //(2) 0x00FF 二进制表示：00000000 11111111 作用：用于提取 低字节（Low Byte）。  
            //(3) 0xFF 二进制表示：00000000 11111111（与 0x00FF 相同） 作用： 用于提取 低 8 位（类似 0x00FF）。 
            byte hi = (byte)((crc & 0xFF00) >> 8); //高位
            byte lo = (byte)(crc & 0x00FF);//低位
            List<byte> temp = new List<byte>(); //List<byte> temp = [.. value, lo, hi];
            temp.AddRange(value);
            temp.Add(lo);
            temp.Add(hi);

            return temp;
        }
    }
}
